package grid
{
	import background.Background;

	import objects.Objects;

	import flash.display.MovieClip;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.KeyboardEvent;
	import flash.events.MouseEvent;
	import flash.geom.Point;

	/**
	 * @author robt
	 */
	public class Grid extends Sprite
	{
		private static const KEY_UP : uint = 38;
		private static const KEY_DOWN : uint = 40;
		private static const KEY_LEFT : uint = 37;
		private static const KEY_RIGHT : uint = 39;
		private static const KEY_A : uint = 65;
		private static const KEY_D : uint = 68;
		private static const KEY_W : uint = 87;
		private static const KEY_S : uint = 83;
		private static const KEY_SMALLER : uint = 190;
		private static const KEY_BIGGER : uint = 188;
		private static const KEY_PLUS : uint = 187;
		private static const KEY_MINUS : uint = 189;
		private var _rows : int = 0;
		private var _cols : int = 0;
		private var _pointer : MovieClip;
		private var _linePreview : Sprite;
		private var _lines : Sprite;
		private var _linePreviewPosition : Point;
		private var _damagePositionDataValues : Array;
		private var _background : Background;
		private var _gridLines : Sprite;
		private var _objects : Objects;
		private var _noDamagePositionDataValues : Array;
		[Embed(source="../assets/assets.swf", symbol="Mine")]
		protected var MineClass : Class;
		protected var _mineIcon : Sprite;
		[Embed(source="../assets/assets.swf", symbol="Pickup")]
		protected var PickupClass : Class;
		protected var _pickupIcon : Sprite;
		[Embed(source="../assets/assets.swf", symbol="Attractor")]
		protected var AttractorClass : Class;
		protected var _attractorIcon : Sprite;
		[Embed(source="../assets/assets.swf", symbol="Repeller")]
		protected var RepellerClass : Class;
		protected var _repellerIcon : Sprite;
		[Embed(source="../assets/assets.swf", symbol="Reactor")]
		protected var ReactorClass : Class;
		protected var _reactorIcon : Sprite;
		[Embed(source="../assets/assets.swf", symbol="Laser")]
		protected var LaserClass : Class;
		protected var _laserIcon : Sprite;
		[Embed(source="../assets/assets.swf", symbol="Hatch")]
		protected var HatchClass : Class;
		protected var _hatchIcon : Sprite;
		[Embed(source="../assets/assets.swf", symbol="ReactorDoor")]
		protected var ReactorDoorClass : Class;
		protected var _reactorDoorIcon : Sprite;
		[Embed(source="../assets/assets.swf", symbol="SwitchDoor")]
		protected var SwitchDoorClass : Class;
		protected var _switchDoorIcon : Sprite;
		[Embed(source="../assets/assets.swf", symbol="ReactorGravity")]
		protected var ReactorGravityClass : Class;
		protected var _reactorGravityIcon : Sprite;

		public function Grid()
		{
			addEventListener(Event.ADDED_TO_STAGE, onAddedToStage);

			_damagePositionDataValues = new Array();
			_noDamagePositionDataValues = new Array();

			_background = new Background(this);
			addChild(_background);

			_objects = new Objects();
			addChild(_objects);

			_gridLines = new Sprite();
			addChild(_gridLines);

			_pointer = new MovieClip();
			_pointer.graphics.beginFill(0x55FF55);
			_pointer.graphics.drawCircle(0, 0, 4);
			addChild(_pointer);

			_linePreview = new Sprite();
			addChild(_linePreview);

			_lines = new Sprite();
			addChild(_lines);

			_mineIcon = Sprite(new (MineClass)());
			_pickupIcon = Sprite(new (PickupClass)());
			_attractorIcon = Sprite(new (AttractorClass)());
			_repellerIcon = Sprite(new (RepellerClass)());
			_reactorIcon = Sprite(new (ReactorClass)());
			_laserIcon = Sprite(new (LaserClass)());
			_hatchIcon = Sprite(new (HatchClass)());
			_reactorDoorIcon = Sprite(new (ReactorDoorClass)());
			_switchDoorIcon = Sprite(new (SwitchDoorClass)());
			_reactorGravityIcon = Sprite(new (ReactorGravityClass)());
		}

		private function onAddedToStage(event : Event) : void
		{
			addEventListeners();
		}

		private function addEventListeners() : void
		{
			stage.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDown);
			stage.addEventListener(MouseEvent.MOUSE_MOVE, onMouseMove);
			stage.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
			stage.addEventListener(MouseEvent.MOUSE_UP, onMouseUp);
			addEventListener(Event.ENTER_FRAME, onEnterFrame);
		}

		private function removeEventListeners() : void
		{
			stage.removeEventListener(KeyboardEvent.KEY_DOWN, onKeyDown);
			stage.removeEventListener(MouseEvent.MOUSE_MOVE, onMouseMove);
			stage.removeEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
			stage.removeEventListener(MouseEvent.MOUSE_UP, onMouseUp);
			removeEventListener(Event.ENTER_FRAME, onEnterFrame);
		}

		public function enable() : void
		{
			addEventListeners();
		}

		public function disable() : void
		{
			removeEventListeners();
		}

		private function onEnterFrame(event : Event) : void
		{
			if (_linePreviewPosition != null && States.drawing)
			{
				_linePreview.graphics.clear();
				_linePreview.graphics.lineStyle(1, 0xFF0000);
				_linePreview.graphics.moveTo(_linePreviewPosition.x, _linePreviewPosition.y);
				_linePreview.graphics.lineTo(_pointer.x, _pointer.y);
			}
		}

		private function onMouseUp(event : MouseEvent) : void
		{
			if (States.drawing)
			{
				if (_linePreviewPosition.x != _pointer.x || _linePreviewPosition.y != _pointer.y)
				{
					if (States.state == States.DRAW_DAMAGE)
					{
						_damagePositionDataValues.push(new PositionData(new Point(_linePreviewPosition.x, _linePreviewPosition.y), new Point(_pointer.x, _pointer.y)));
					}
					else
					{
						_noDamagePositionDataValues.push(new PositionData(new Point(_linePreviewPosition.x, _linePreviewPosition.y), new Point(_pointer.x, _pointer.y)));
					}
				}

				_linePreviewPosition = null;
				_linePreview.graphics.clear();

				drawAllLines();
			}
		}

		public function drawAllLines() : void
		{
			_lines.graphics.clear();
			_lines.graphics.lineStyle(1, 0xFF0000);

			for each (var damagePositionData : PositionData in _damagePositionDataValues)
			{
				_lines.graphics.moveTo(damagePositionData.startPosition.x, damagePositionData.startPosition.y);
				_lines.graphics.lineTo(damagePositionData.endPosition.x, damagePositionData.endPosition.y);
			}

			_lines.graphics.lineStyle(1, 0x0000FF);

			for each (var noDamagePositionData : PositionData in _noDamagePositionDataValues)
			{
				_lines.graphics.moveTo(noDamagePositionData.startPosition.x, noDamagePositionData.startPosition.y);
				_lines.graphics.lineTo(noDamagePositionData.endPosition.x, noDamagePositionData.endPosition.y);
			}
		}

		private function onMouseDown(event : MouseEvent) : void
		{
			if (States.drawing)
			{
				_linePreviewPosition = new Point(_pointer.x, _pointer.y);
			}
			else if (States.state == States.PICK_UP)
			{
				_objects.addPickup(new Point(_pointer.x, _pointer.y));
			}
			else if (States.state == States.MINE)
			{
				_objects.addMine(new Point(_pointer.x, _pointer.y));
			}
			else if (States.state == States.REPELLER)
			{
				_objects.addRepeller(new Point(_pointer.x, _pointer.y));
			}
			else if (States.state == States.ATTRACTOR)
			{
				_objects.addAttractor(new Point(_pointer.x, _pointer.y));
			}
			else if (States.state == States.REACTOR)
			{
				_objects.addReactor(new Point(_pointer.x, _pointer.y));
			}
			else if (States.state == States.LASER)
			{
				_objects.addLaser(new Point(_pointer.x, _pointer.y));
			}
			else if (States.state == States.REACTOR_DOOR)
			{
				_objects.addReactorDoor(new Point(_pointer.x, _pointer.y));
			}
			else if (States.state == States.HATCH)
			{
				_objects.addHatch(new Point(_pointer.x, _pointer.y));
				_objects.addPlayer(new Point(_pointer.x + (States.hatchSize / 2), _pointer.y - 32));
			}
			else if (States.state == States.REACTOR_GRAVITY)
			{
				_objects.addReactorGravity(new Point(_pointer.x, _pointer.y));
			}
			else if (States.state == States.SWITCH_DOOR)
			{
				_objects.addSwitchDoor(new Point(_pointer.x, _pointer.y));
			}
		}

		private function onMouseMove(event : MouseEvent) : void
		{
			updateMousePointer();
		}

		private function updateMousePointer() : void
		{
			var mx : Number = mouseX + States.gridSize / 2;
			var my : Number = mouseY + States.gridSize / 2;

			_pointer.x = mx - mx % States.gridSize;
			_pointer.y = my - my % States.gridSize;

			while (_pointer.numChildren > 0)
			{
				_pointer.removeChildAt(0);
			}

			if (States.state == States.MINE)
			{
				_pointer.addChild(_mineIcon);
			}
			else if (States.state == States.PICK_UP)
			{
				_pointer.addChild(_pickupIcon);
			}
			else if (States.state == States.ATTRACTOR)
			{
				_pointer.addChild(_attractorIcon);
			}
			else if (States.state == States.REPELLER)
			{
				_pointer.addChild(_repellerIcon);
			}
			else if (States.state == States.REACTOR)
			{
				_pointer.addChild(_reactorIcon);
			}
			else if (States.state == States.LASER)
			{
				_laserIcon.rotation = 0;
				_laserIcon.width = States.laserWidth;
				_laserIcon.rotation = States.laserHorizontal ? 0 : 90;
				_pointer.addChild(_laserIcon);
			}
			else if (States.state == States.REACTOR_DOOR)
			{
				_reactorDoorIcon.rotation = 0;
				_reactorDoorIcon.width = States.reactorDoorWidth;
				_reactorDoorIcon.rotation = States.reactorDoorHorizontal ? 0 : 90;
				_pointer.addChild(_reactorDoorIcon);
			}
			else if (States.state == States.SWITCH_DOOR)
			{
				_switchDoorIcon.rotation = 0;
				_switchDoorIcon.width = States.switchDoorWidth;

				if (States.switchDoorHorizontal && States.switchDoorTopLeft)
				{
					_switchDoorIcon.scaleY = -1;
				}
				else if (!States.switchDoorHorizontal)
				{
					_switchDoorIcon.rotation = 90;
					if (States.switchDoorTopLeft)
					{
						_switchDoorIcon.scaleY = -1;
					}
				}

				_pointer.addChild(_switchDoorIcon);
			}
			else if (States.state == States.HATCH)
			{
				_hatchIcon.width = States.hatchSize;
				_pointer.addChild(_hatchIcon);
			}
			else if (States.state == States.REACTOR_GRAVITY)
			{
				_pointer.addChild(_reactorGravityIcon);
			}
		}

		private function onKeyDown(event : KeyboardEvent) : void
		{
			switch(event.keyCode)
			{
				case  KEY_UP:
				case  KEY_W:
					y += 64;
					break;
				case  KEY_DOWN:
				case  KEY_S:
					y -= 64;
					break;
				case  KEY_LEFT:
				case  KEY_A:
					x += 64;
					break;
				case  KEY_RIGHT:
				case  KEY_D:
					x -= 64;
					break;
				case  KEY_SMALLER:
					States.gridSize -= 8;
					updateGrid();
					break;
				case  KEY_BIGGER:
					States.gridSize += 8;
					updateGrid();
					break;
				case KEY_PLUS:
					zoomIn();
					break;
				case KEY_MINUS:
					zoomOut();
					break;
			}

			updateMousePointer();
		}

		private function zoomOut() : void
		{
			var prevWidth : Number = width;
			var prevHeight : Number = height;
			scaleX = scaleY = (scaleX - .1);
			x -= (width - prevWidth) / 2;
			y -= (height - prevHeight) / 2;
		}

		private function zoomIn() : void
		{
			var prevWidth : Number = width;
			var prevHeight : Number = height;
			scaleX = scaleY = (scaleX + .1);
			x -= (width - prevWidth) / 2;
			y -= (height - prevHeight) / 2;
		}

		public function drawGrid() : void
		{
			_gridLines.graphics.clear();
			_gridLines.graphics.lineStyle(1, 0x000000, 0.25);

			for (var row : int = 0; row <= _rows; row++)
			{
				_gridLines.graphics.moveTo(0, row * States.gridSize);
				_gridLines.graphics.lineTo(_cols * States.gridSize, row * States.gridSize);
			}

			for (var col : int = 0; col <= _cols; col++)
			{
				_gridLines.graphics.moveTo(col * States.gridSize, 0);
				_gridLines.graphics.lineTo(col * States.gridSize, _rows * States.gridSize);
			}

			_background.y = (_gridLines.height - _background.height);
			_objects.y = _background.y;
		}

		public function getYOffset() : Number
		{
			return _gridLines.height - 1;
		}

		public function getPointerPosition() : Point
		{
			return new Point(_pointer.x, (_rows * States.gridSize) - _pointer.y);
		}

		public function reset() : void
		{
			_objects.reset();
			_damagePositionDataValues = new Array();
			_noDamagePositionDataValues = new Array();
			drawAllLines();
		}

		public function undo() : void
		{
			if (States.drawing)
			{
				if (States.state == States.DRAW_DAMAGE)
				{
					_damagePositionDataValues.pop();
				}
				else
				{
					_noDamagePositionDataValues.pop();
				}

				drawAllLines();
			}
			else
			{
				_objects.undo();
			}
		}

		public function onImageLoaded() : void
		{
			reset();

			_rows = Math.ceil(_background.height / States.gridSize);
			_cols = Math.ceil(_background.width / States.gridSize);

			drawGrid();

			_objects.yOffset = getYOffset();
			_background.y = (_gridLines.height - _background.height);
			_objects.y = _background.y;
		}

		public function loadImage(url : String) : void
		{
			_background.loadImage(url);
		}

		public function getDamagePositionDataValues() : Array
		{
			return _damagePositionDataValues;
		}

		public function getNoDamagePositionDataValues() : Array
		{
			return _noDamagePositionDataValues;
		}

		public function getObjects() : Array
		{
			return _objects.getObjects();
		}

		public function updateGrid() : void
		{
			_rows = Math.ceil(_background.height / States.gridSize);
			_cols = Math.ceil(_background.width / States.gridSize);

			drawGrid();

			_objects.yOffset = getYOffset();
		}
	}
}
